#include "StdAfx.h"
#include "draw_obj.h"
#include "common_definitions.h"
#include "boost\math\special_functions\round.hpp"
#include "handle_count_visitor.h"
#include "handle_position_visitor.h"
#include "cursor_visitor.h"
#include "bounds_visitor.h"
#include "move_visitor.h"
#include "draw_visitor.h"
#include "hit_test_visitor.h"
#include "intersects_visitor.h"
#include "move_handle_to_visitor.h"

boost::function<void (long, const point_2&, int, int, const color_t&)> draw_obj::DrawCross;
boost::function<void (long, const box_2&, int, const color_t&)> draw_obj::DrawBox;
boost::function<void (long, const angle_box_2&, int, const color_t&)> draw_obj::DrawAngleBox;
boost::function<void (long, const segment_2&, int, const color_t&)> draw_obj::DrawSegment;
boost::function<void (long, const arc_2&, int, const color_t&)> draw_obj::DrawArc;
boost::function<void (long, const ring_2&, int, const color_t&)> draw_obj::DrawRing;
boost::function<void (long, const annulus_2&, int, const color_t&)> draw_obj::DrawAnnulus;
boost::function<void (long, const annulus_arc_2&, int, const color_t&)> draw_obj::DrawAnnulusSector;
boost::function<void (long, const polygon_2&, int, const color_t&)> draw_obj::DrawPolygon;

int draw_obj::sStdTolerance    = 6;
int draw_obj::sStdLineDelta    = 1;
int draw_obj::sStdPageDelta    = 5;
int draw_obj::sStdArrowHeight  = 1;
int draw_obj::sStdArrowWidth   = 1;
int draw_obj::sStdMarkerSize   = 10;
int draw_obj::sStdHandleSize   = 5;

color_t draw_obj::sBorderColor = color_t(0, 0, 255);
color_t draw_obj::sHandleColor = color_t(255, 0, 0);

int draw_obj::sTolerance       = draw_obj::sStdTolerance;
int draw_obj::sLineDelta       = draw_obj::sStdLineDelta;
int draw_obj::sPageDelta       = draw_obj::sStdPageDelta;
int draw_obj::sArrowHeight     = draw_obj::sStdArrowHeight;
int draw_obj::sArrowWidth      = draw_obj::sStdArrowWidth;
int draw_obj::sMarkerSize      = draw_obj::sStdMarkerSize;
int draw_obj::sHandleSize      = draw_obj::sStdHandleSize;
int draw_obj::sBorderWidth     = 1;

const int draw_obj::kBorderCursors[8]	= \
{
  kCursorSizeWE,
  kCursorSizeNESW,
  kCursorSizeNS,
  kCursorSizeNWSE,
  kCursorSizeWE,
  kCursorSizeNESW,
  kCursorSizeNS,
  kCursorSizeNWSE
};

const vector_2 draw_obj::kDivisionVectors[8] = \
{
  vector_2( 0.9238795325113,  0.3826834323651),
  vector_2( 0.3826834323651,  0.9238795325113),
  vector_2(-0.3826834323651,  0.9238795325113),
  vector_2(-0.9238795325113,  0.3826834323651),
  vector_2(-0.9238795325113, -0.3826834323651),
  vector_2(-0.3826834323651, -0.9238795325113),
  vector_2( 0.3826834323651, -0.9238795325113),
  vector_2( 0.9238795325113, -0.3826834323651)
};

draw_obj::draw_obj(void)
{
  OwnerElement  = -1;
  owner_wnd_id_ = -1;
}

draw_obj::draw_obj(const tstring& objName, const shape_2& objData,
  long ownerWndID, int ownerElement)
  : name_(objName)
  , data_(objData)
  , owner_wnd_id_(ownerWndID)
  , OwnerElement(ownerElement)
{
  recalc_bounds();
}

draw_obj::~draw_obj(void)
{
}

draw_obj::style::style(void)
  : border_color(color_t(0, 0, 255))
  , handle_color(color_t(255, 0, 0))
  , line_width(1)
  , border_style(0)
{
}

void draw_obj::add_point(const point_2& pt)
{
  if (polygon_2* polygon = boost::get<polygon_2>(&data_)) {
    polygon->push_back(pt);
    recalc_bounds();
  }
}

point_2 draw_obj::get_point(int pos) const
{
  if (const polygon_2* polygon = boost::get<polygon_2>(&data_)) {
    return polygon->at(pos);
  }

  return point_2();
}

void draw_obj::pop_back(void)
{
  if (polygon_2* polygon = boost::get<polygon_2>(&data_)) {
    polygon->pop_back();
    recalc_bounds();
  }
}

int draw_obj::num_points(void) const
{
  if (const polygon_2* polygon = boost::get<polygon_2>(&data_)) {
    return polygon->size();
  }

  return 0;
}

int draw_obj::get_handle_count(void) const
{
  return boost::apply_visitor(handle_count_visitor(), data_);
}

point_2 draw_obj::get_handle(int handle_id) const
{
  return boost::apply_visitor(handle_position_visitor(handle_id), data_);
}

box_2 draw_obj::get_handle_box(int handle_id) const
{
  point_2 handle_position = \
    boost::apply_visitor(handle_position_visitor(handle_id), data_);

  return box_2(handle_position, sHandleSize);
}

int draw_obj::get_cursor(int hit_code, const point_2& point) const
{
  return boost::apply_visitor(cursor_visitor(hit_code, point), data_);
}

void draw_obj::recalc_bounds(void)
{
  position_ = boost::apply_visitor(bounds_visitor(), data_);
}

void draw_obj::move_to(const box_2& position)
{
  if (position == position_) {
    return;
  }

  boost::apply_visitor(move_visitor(position.top_left-\
    position_.top_left), data_);
  position_ = position;
}

void draw_obj::draw(long window_id) const
{
  boost::apply_visitor(draw_visitor(window_id, style_.line_width,
    style_.border_color), data_);
}

void draw_obj::draw_tracker(long window_id, int state) const
{
  if (state!=kTrackerSelected && state!=kTrackerActive) {
    return;
  }

  int handleCount = get_handle_count();
  for (int nHandle=0; nHandle<handleCount; ++nHandle) {
    box_2 handleBox = get_handle_box(nHandle);
    DrawBox(window_id, handleBox, style_.line_width, style_.handle_color);
  }
}

int draw_obj::hit_test(const point_2& point, bool selected) const
{
  return boost::apply_visitor(hit_test_visitor(point, sTolerance,
    selected), data_);
}

bool draw_obj::intersects(const box_2& box) const
{
  return boost::apply_visitor(intersects_visitor(box), data_);
}

void draw_obj::move_handle_to(int handle, const point_2& point)
{
  return boost::apply_visitor(move_handle_to_visitor(handle, point), data_);
}

draw_obj* draw_obj::clone(void) const
{
  draw_obj* cloned = new draw_obj(name_, data_, owner_wnd_id_, OwnerElement);
  cloned->position_ = position_;

  return cloned;
}

void draw_obj::scale(double sf)
{
  sTolerance    = boost::math::iround(sStdTolerance*sf);
  sLineDelta    = boost::math::iround(sStdLineDelta*sf);
  sPageDelta    = boost::math::iround(sStdPageDelta*sf);
  sArrowHeight  = boost::math::iround(sStdArrowHeight*sf);
  sArrowWidth   = boost::math::iround(sStdArrowWidth*sf);
  sMarkerSize   = boost::math::iround(sStdMarkerSize*sf);
  sHandleSize   = boost::math::iround(sStdHandleSize*sf);
}

draw_obj::QuadrantCode draw_obj::quadrant(const point_2& origin,
  const point_2& point)
{
  vector_2 vt(point.x-origin.x, origin.y-point.y);
  bool bFirst = vt.left(kDivisionVectors[7]);
  bool bLast	= vt.left(kDivisionVectors[0]);

  if(bFirst && !bLast) {
    return kQuadrantI;
  }

  bFirst = bLast;
  bLast = vt.left(kDivisionVectors[1]);
  if(bFirst && !bLast) {
    return kQuadrantII;
  }

  bFirst = bLast;
  bLast = vt.left(kDivisionVectors[2]);
  if(bFirst && !bLast) {
    return kQuadrantIII;
  }

  bFirst = bLast;
  bLast = vt.left(kDivisionVectors[3]);
  if(bFirst && !bLast) {
    return kQuadrantIV;
  }

  bFirst = bLast;
  bLast = vt.left(kDivisionVectors[4]);
  if(bFirst && !bLast) {
    return kQuadrantV;
  }

  bFirst = bLast;
  bLast = vt.left(kDivisionVectors[5]);
  if(bFirst && !bLast) {
    return kQuadrantVI;
  }

  bFirst = bLast;
  bLast = vt.left(kDivisionVectors[6]);
  if(bFirst && !bLast) {
    return kQuadrantVII;
  }

  return kQuadrantVIII;
}

draw_obj::QuadrantCode draw_obj::quadrant(int angle)
{
  return static_cast<QuadrantCode>(static_cast<int>((angle-22.5)/45+9)%8);
}
